#!/usr/bin/python
#
# Copyright (C) 2008 rambla.be
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
""" @file
@brief Implements RassService.
"""

try:
  from xml.etree import cElementTree as ElementTree
except ImportError:
  try:
    import cElementTree as ElementTree
  except ImportError:
    from elementtree import ElementTree
import os
import urllib
import atom
import rawsc
import rawsc.service
from rawsc import rass
from rawsc.rass import dir, user, meta

class RassService(rawsc.service.RawsService):
    """ @brief RassService extends RawsService to provide an interface for RASS requests.
    """

    def __init__(self, username=None, password=None, source=None, server=None, port = None, additional_headers=None, handler=None):
        super(RassService, self).__init__(username = username, password = password, source = source, server = server, port = port, additional_headers = additional_headers, handler = handler)

    def delete(self, uri):
        """ Deletes any resource, given the uri. """
        return self.Delete(uri = uri)


    def getDirFeed(self, uri, query = None):
        """ Retrieves a feed of dirs or items. 
        
            @param uri URI of full URI path to the dir instance.
            @param query rawsc.Query object that contains queryset args.
            @return DirFeed object
        """
        if uri is None:
          raise rawsc.RawscException('You must provide a valid URI argument to getDirFeed().')
        if query:
            query.feed = uri
            uri = query.ToUri()
        return self.Get(uri = uri, converter=rass.dir.DirFeedFromString)

    def getDirFeedFromPath(self, path, query = None):
        """ Retrieves a dir entry. 

            @param path : relative path to the dir instance, starting from the user's CDN rootdir.
            @param query rawsc.Query object that contains queryset args.
            @return DirFeed object
        """
        uri = "/dir/"
        if path:
            uri += path.lstrip("/")
        return self.getDirFeed(uri = uri, query = query)
        
    def createDir(self, path):
        """ Tries to POST a new dir resource to RASS, containing the params that were passed as string args.

            @param path : relative path from the root dir to the dir to be created
            @return DirEntry object
        """
        uri = "/dir/" + path.lstrip("/")
        return self.Put(data = None, uri= uri, converter= rass.dir.DirEntryFromString)

    def postItem(self, dirpath, filename, local_path):
        """ Tries to POST a new item resource to RASS. 
            Note that POST URI path shouldn't contain the filename. Therefore, the first param is named dirpath.
            Note also that if the file already exists, RASS will add a numerical suffix to the filename and create it anyway.
            Therefore, you should check the response (return value) for the exact filename.

            @param path : path to the directory (on the rambla CDN) in which the item needs to be created.
            @param filename proposed filename to be used when storing the file (RASS may append a suffix if file already exists on CDN).
            @param local_path location of the file to be uploaded on the local machine

            @return DirEntry object
        """
        uri = "/item/" + dirpath.lstrip("/")
        media_source = rawsc.MediaSource(file_path = local_path, svr_filename = filename)
        media_entry = self.Post(data = None, uri = uri, media_source = media_source, converter=rass.dir.DirEntryFromString)
        return media_entry

    def createItem(self, path, local_path):
        """ Tries to PUT a new item resource to RASS.
            Note that PUT URI path should contain the filename.
            If the file already exists, PUT will fail.

            @param path : path to be given to the uploaded file on the RASS server.
            @param local_path location of the file to be uploaded on the local machine

            @return DirEntry object
        """
        uri = "/item/" + path.lstrip("/")
        media_source = rawsc.MediaSource(file_path = local_path, svr_filename = None)
        media_entry = self.Put(data = None, uri = uri, media_source = media_source, converter=rass.dir.DirEntryFromString)
        return media_entry

    def getItemFile(self, uri, local_path = None):
        """ Downloads a item file and (optionally) saves it to a local file location. 
        
            If local_path is given, an attempt is made to save the downloaded file and an exception is thrown in case of failure.
            If local_path is None, the downloaded file is not stored. The caller should store it himself using the file_handle attribute of the returned MediaSource object.
        
            @param uri path (relative or absolute) to the item media item.
            @param local_path full path, including the filename, to a location where the downloaded file can be stored.
            @return rawsc.MediaSource object
        """
        if uri is None:
          raise rawsc.RawscException('You must provide a valid URI argument to getItemFile().')
        return self.GetMedia(uri = uri, file_path = local_path)

    def getItemFromPath(self, path, local_path = None):
        """ Downloads a item file and (optionally) saves it to a local file location. 
        
            If local_path is given, an attempt is made to save the downloaded file and an exception is thrown in case of failure.
            If local_path is None, the downloaded file is not stored. The caller should store it himself using the file_handle attribute of the returned MediaSource object.
        
            @param path : relative path to the dir instance, starting from the root.
            @param local_path full path, including the filename, to a location where the downloaded file can be stored.
            @return rawsc.MediaSource object
        """
        return self.getItemFile(uri = "/item/" + path.lstrip("/"), local_path = local_path)

    def getMeta(self, uri, query = None, entry_only = False, feed_only = False):
        """ Retrieves a meta entry or feed. 

            @param uri URI of full URI path to the meta instance ("/meta/{username}/{path_to_file_or_dir}").
            @param query rawsc.Query object that contains queryset args.
            @param entry_only : if True, None will be returned if the given uri returns a feed (instead of an entry).
            @param feed_only : if True, None will be returned if the given uri returns an entry (instead of a feed).
            @return MetaEntry object
        """
        if uri is None:
          raise rawsc.RawscException('You must provide a valid URI argument to getMeta().')
        # add query if desired
        if query:
            query.feed = uri
            uri = query.ToUri()
        # try to get entry/feed response
        response = None
        if not entry_only:
            # meta can both return a feed or an entry => try feed converter first
            response = self.Get(uri, converter=rass.meta.MetaFeedFromString)
        if response is None and not feed_only:
            # failed to convert to feed, try entry converter
            response = self.Get(uri, converter=rass.meta.MetaEntryFromString)
        return response

    def getMetaFromPath(self, path, query = None, entry_only = False, feed_only = False, alt_username = None):
        """ Retrieves a meta entry. 

            @param path : relative path to the meta instance, starting from the user's CDN rootdir (/{path_to_file_or_dir}").
            @param query rawsc.Query object that contains queryset args.
            @param entry_only : if True, None will be returned if the given uri returns a feed (instead of an entry).
            @param feed_only : if True, None will be returned if the given uri returns an entry (instead of a feed).
            @param alt_username : Alternative name (= different form the authentication name) for the user on the CDN.
            @return MetaEntry object
        """
        uripath = "/meta/"
        if alt_username is not None:
            uripath = uripath + alt_username
        else:
            uripath = uripath + self.username
        uripath = uripath + "/" + path.lstrip("/")
        return self.getMeta(uri = uripath, query = query, entry_only = entry_only, feed_only = feed_only)

    def getUser(self, uri):
        """ Retrieves an user entry. 

            @param uri path (relative or absolute) to the user instance.
            @return UserEntry object
        """
        if uri is None:
          raise rawsc.RawscException('You must provide a valid URI argument to getUser().')
        return self.Get(uri, converter=rass.user.UserEntryFromString)

    def createUser(self, username, email, first_name=None, last_name=None, password = None):
        """ Tries to POST a new user resource to RATS, containing the params that were passed as string args.

            @return UserEntry object
        """
        params = rass.user.UserParams(username=username, email=email, first_name=first_name, last_name=last_name, password = password)
        content = rass.user.UserContent(params = params)
        entry = rass.user.UserEntry(content = content)
        return self.Post(entry, uri= "/user/", converter= rass.user.UserEntryFromString)

    def updateUser(self, entry, uri = None):
        """ Update an existing user profile, using a PUT request.

            @param entry: the complete entry, containing the changed params
            @param uri location of the instance to be updated, if None it will be retrieved from the entry
        """
        if uri is None:
            uri = entry.id.text
        if not uri:
            raise rawsc.RawscException('Entry is not valid.')
        return self.Put(uri = uri, data = entry, converter = rass.user.UserEntryFromString)
